package com.lei.app.handler;

import com.lei.app.provider.WsRoomProvider;
import com.lei.app.websocket.AbstractWsEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.nutz.Nutz;
import org.nutz.dao.Dao;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.nutz.lang.Strings;
import org.nutz.lang.reflect.FastClassFactory;
import org.nutz.lang.reflect.FastMethod;
import org.nutz.lang.util.Callback;
import org.nutz.lang.util.NutMap;

import javax.servlet.http.HttpSession;
import javax.websocket.Session;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author 郑兴泉 956607644@qq.com
 * @data 2020/4/9
 * 描述：
 */
@Slf4j
public class AbstractWsHandler implements WsHandler {

    protected Dao dao;
    
    /***
     * 房间集合
     */
    protected Set<String> rooms;
    /***
     * 房间与人员信息
     */
    protected WsRoomProvider roomProvider;

    protected Session session;
    protected String prefix;
    protected HttpSession httpSession;
    protected AbstractWsEndpoint endpoint;
    protected Map<String, Callback<NutMap>> actions = new HashMap<>();

    public AbstractWsHandler(String prefix) {
        rooms = new HashSet<>();
        this.prefix = prefix;
    }

    public void join(String room) {
        if (!Strings.isBlank(room)) {
            rooms.add(room);
            room = prefix + room;
            log.debug("session(id=%s) join room(name=%s)", session.getId(), room);
            roomProvider.join(room, session.getId());
        }
    }

    public void remove(String room) {
        if (!Strings.isBlank(room)) {
            rooms.remove(room);
            room = prefix + room;
            log.debug("session(id=%s) left room(name=%s)", session.getId(), room);
            roomProvider.remove(room, session.getId());
        }
    }


    @Override
    public void depose() {
        for (Object room : rooms.toArray()) {
            remove(String.valueOf(room));
        }
    }


    @Override
    public void init() {
        // 遍历所有public方法
        for (final Method method : getClass().getMethods()) {
            Class<?>[] paramTypes = method.getParameterTypes();
            // 如果仅一个参数,且参数类型NutMap的话, 就判定是WsAction方法咯
            if (paramTypes.length == 1 && NutMap.class.isAssignableFrom(paramTypes[0])) {
                // 如果有FastMethod实现的话...
                if (Nutz.majorVersion() == 1 && Nutz.minorVersion() > 60) {
                    final FastMethod fm = FastClassFactory.get(method);
                    actions.put(method.getName(), new Callback<NutMap>() {
                        @Override
                        public void invoke(NutMap msg) {
                            try {
                                fm.invoke(AbstractWsHandler.this, msg);
                            }
                            catch (Throwable e) {
                                onActionError(msg, e);
                            }
                        }
                    });
                }
                // 老版本的话...
                else {
                    actions.put(method.getName(), new Callback<NutMap>() {
                        @Override
                        public void invoke(NutMap msg) {
                            try {
                                method.invoke(AbstractWsHandler.this, msg);
                            }
                            catch (Throwable e) {
                                onActionError(msg, e);
                            }
                        }
                    });
                }
            }
        }
    }


    /**
     * 处理消息, 将其转为NutMap,然后找对应的处理方法.
     */
    @Override
    public void onMessage(String message) {
        try {
            NutMap msg = Json.fromJson(NutMap.class, message);
            String action = msg.getString("action");
            if (Strings.isBlank(action)) {
                return;
            }
            Callback<NutMap> at = actions.get(action);
            if (at != null) {
                at.invoke(msg);
            } else {
                defaultAction(msg);
            }
        }
        catch (Throwable e) {
            onActionError(null, e);
        }
    }

    /**
     * 抛出异常的时候调用之
     */
    public void onActionError(NutMap msg, Throwable e) {
        if (log.isInfoEnabled()) {
            log.info("bad message ? msg=%s",
                    Json.toJson(msg, JsonFormat.compact().setIgnoreNull(false)),
                    e);
        }
    }

    /**
     * 没有任何action方法对应时,就调用它咯
     */
    public void defaultAction(NutMap msg) {
        if (log.isDebugEnabled()) {
            log.debug("unknown action msg = %s",
                    Json.toJson(msg, JsonFormat.compact().setIgnoreNull(false)));
        }
    }

    @Override
    public void setRoomProvider(WsRoomProvider roomProvider) {
        this.roomProvider = roomProvider;
    }

    @Override
    public void setSession(Session session) {
        this.session = session;
    }

    @Override
    public void setHttpSession(HttpSession httpSession) {
        this.httpSession = httpSession;
    }

    @Override
    public void setEndpoint(AbstractWsEndpoint endpoint) {
        this.endpoint = endpoint;
    }

    public Dao getDao() {
        return dao;
    }

    public void setDao(Dao dao) {
        this.dao = dao;
    }

    protected String getSessionId(){
        return  session.getId();
    }
}
